import { AfterViewInit, Component, Input, OnDestroy, OnInit, ChangeDetectorRef } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Subject } from 'rxjs';
import { EzFormControl } from '../ez-form-control';
import { EzFormGroup, EzFormGroupComponent, EzFormGroupConfiguration } from '../ez-form-group';
import { EzForm } from './ez-form';
import { EzFormConfiguration } from './ez-form.configuration';
import { EzFormConfigurationValidator } from '../../utils';
@Component({
  selector: 'ez-form',
  templateUrl: './ez-form.component.html',
  styleUrls: ['./ez-form.component.less'],
})
export class EzFormComponent implements EzForm, OnInit, OnDestroy, AfterViewInit {
  @Input()
  ezFormConfiguration: EzFormConfiguration;

  formGroup: FormGroup = new FormGroup({});

  private ezFormGroups: { [name: string]: EzFormGroup } = {};

  private destroy$ = new Subject<any>();

  initialized = false;

  submitLoading = false;

  constructor(private formBuilder: FormBuilder, private cdf: ChangeDetectorRef) {}

  ngOnInit(): void {
    this.checkConfiguration(this.ezFormConfiguration);
  }

  ngAfterViewInit(): void {
    this.initialized = true;
    if (this.ezFormConfiguration.onInit) {
      setTimeout(() => this.ezFormConfiguration.onInit(this), 0);
      // this.cdf.detectChanges();
    }
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }

  getControl<C extends EzFormControl = EzFormControl>(name: string): C {
    for (const group of Object.values(this.ezFormGroups)) {
      if (group.contains(name)) {
        return group.get(name);
      }
    }
    return null;
  }

  containsControl(name: string): boolean {
    for (const group of this.ezFormConfiguration.groups) {
      for (const control of group.controls) {
        if (control.name === name) {
          return true;
        }
      }
    }
    return false;
  }

  disableControls(names?: string | string[]): void {
    if (names) {
      names = Array.isArray(names) ? names : [names];
      names.forEach(name => {
        Object.values(this.ezFormGroups).forEach(g => {
          if (g.contains(name)) {
            g.get(name).disable();
          }
        });
      });
    }
  }

  enableControls(names?: string | string[]): void {
    if (names) {
      names = Array.isArray(names) ? names : [names];
      names.forEach(name => {
        Object.values(this.ezFormGroups).forEach(g => {
          if (g.contains(name)) {
            g.get(name).enable();
          }
        });
      });
    }
  }

  removeControl(name: string): void {
    for (const group of this.ezFormConfiguration.groups) {
      for (const control of group.controls) {
        if (control.name === name) {
          this.ezFormGroups[group.name].remove(name);
        }
      }
    }
  }

  getGroup(name: string): EzFormGroup {
    return this.ezFormGroups[name] || null;
  }

  groups(): { [name: string]: EzFormGroup } {
    return this.ezFormGroups;
  }

  removeGroup(name: string): void {
    if (this.ezFormConfiguration.groups.find(g => g.name === name)) {
      this.ezFormConfiguration.groups = this.ezFormConfiguration.groups.filter(g => g.name !== name);
    } else {
      throw new Error(`不存在控件组[${name}]`);
    }
  }

  addGroup(configuration: EzFormGroupConfiguration, index?: number): void {
    if (index && (index < 0 || index > this.ezFormConfiguration.groups.length)) {
      throw new Error(`控件插入位置参数非法`);
    }
    index = index === 0 ? 0 : index ? index : this.ezFormConfiguration.groups.length;
    EzFormConfigurationValidator.validateGroup(configuration);
    if (this.ezFormConfiguration.groups.find(g => g.name === configuration.name)) {
      throw new Error(`控件组[${configuration.name}]已经存在`);
    }
    const controlNames = configuration.controls.map(c => c.name);
    controlNames.forEach(name => {
      if (this.containsControl(name)) {
        throw new Error(`控件[${name}]已经存在`);
      }
    });
    this.ezFormConfiguration.groups.splice(index, 0, configuration);
    this.ezFormConfiguration.groups = [...this.ezFormConfiguration.groups];
  }

  disableGroups(names?: string | string[]): void {
    if (names) {
      names = Array.isArray(names) ? names : [names];
      Object.keys(this.ezFormGroups)
        .filter(name => names.includes(name))
        .forEach(name => this.ezFormGroups[name].disable());
    }
  }
  enableGroups(names?: string | string[]): void {
    if (names) {
      names = Array.isArray(names) ? names : [names];
      Object.keys(this.ezFormGroups)
        .filter(name => names.includes(name))
        .forEach(name => this.ezFormGroups[name].enable());
    }
  }

  reset(): void {
    this.formGroup.reset();
  }

  getValue(): { [name: string]: any } {
    return this.formGroup.getRawValue();
  }

  validate(): void {
    for (const control of Object.values(this.formGroup.controls)) {
      control.markAsDirty();
      control.updateValueAndValidity();
    }
  }

  isValid(): boolean {
    this.validate();
    return this.formGroup.valid;
  }

  addEzFormGroupComponent(name: string, control: EzFormGroupComponent): void {
    this.ezFormGroups[name] = control;
  }

  removeEzFormGroupComponent(name: string): void {
    delete this.ezFormGroups[name];
  }

  private checkConfiguration(config: EzFormConfiguration): void {
    EzFormConfigurationValidator.validateForm(config);
  }

  onReset(): void {
    if (this.ezFormConfiguration.onReset) {
      this.ezFormConfiguration.onReset(this);
    } else {
      this.reset();
    }
  }

  onSubmit(): void {
    this.validate();
    if (this.formGroup.valid) {
      if (this.ezFormConfiguration.onSubmit) {
        this.submitLoading = true;
        this.ezFormConfiguration.onSubmit(this).subscribe(
          r => {
            this.submitLoading = false;
            console.log(this.submitLoading);
          },
          e => {
            this.submitLoading = false;
            console.error(e);
          }
        );
      }
    }
  }
}
